export = {};
/*
一个常见的任务是将一个已知的类型每个属性都变为可选的：
interface PersonPartial {
    name?: string;
    age?: number;
}
或者我们想要一个只读版本：
interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}

// 这种情况在js之中经常发生
// TypeScript提供了从旧类型中创建新类型的一种方式 — 映射类型(编程里的循环)。
// 在映射类型里，新类型以相同的形式去转换旧类型里每个属性。
// 例如，你可以令每个属性成为 readonly类型或可选的。
//
// This happens often enough in JavaScript that TypeScript provides a way to create new types based on old types — mapped types. In a mapped type, the new type transforms each property in the old type in the same way. For example, you can make all properties of a type readonly or optional. Here are a couple of examples

type Readonly<T> = {
  //咋循环呢？
  //用 [] 即可, 表示我要做循环
  readonly [P in keyof T]: T[P];
  //↑ 循环T里的每个key
}
//↑你可以理解成↓
// for(P in keyof T){
//   readonly [P]:T[P]
// }

type Partial<T> = {
  [P in keyof T]?: T[P];
}

需要注意的是 上面 `[P in keyof T]?: T[P];` 这类语法 不是在描述一个成员,而是在描述一个完整的类型

如果你想给上面的Partial<T>添加成员,需要这样

type PartialWithNewMember<T> = {
  [P in keyof T]?: T[P];
} & { newMember: boolean }

而不是这样:
type PartialWithNewMember<T> = {
  [P in keyof T]?: T[P];
  newMember: boolean;
}
*/


/** 1. Readonly*/
interface Person {
  name: string;
  age: number;
}

interface ReadonlyPerson {
  readonly name: string;
  readonly age: number;
}

type ReadonlyPerson2 = Readonly<Person>;
/* ↑ Readonly 是一个快捷方法 能帮我们把一个接口快速转换为成员全部为readonly的接口(并不会改变原接口)
type ReadonlyPerson2 = {
  readonly name: string;
  readonly age: number;
}

//ctrl+左键点击 Readonly 可查看源码
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

P in keyof T 相当于一个循环操作
↓↓
for(P in keyof T){
  readonly [P]:T[P]
}
*/




/** 2. Partial*/
interface Person2 {
  name: string;
  age: number;
  grade: number;
}

interface Person3 {
  name?: string;
  age?: number;
  grade?: number;
}

type Person4 = Partial<Person2>
/*↑
type Person4 = {
  name?: string;
  age?: number;
  grade?: number;
} */




/** 3. Required*/
type Required<T> = {
  //-? 表示去掉可选
  [P in keyof T]-?: T[P]
}
type ReadonlyType = Required<Person3>; //{name: string, age: number, grade: number}


// 以前, ↑ 的这些方法都是在类型中挑选类型
// , 我们的Omit和Pick是在对象中挑选和忽略
// , 这俩概念是完全不一样的

/** 4. Pick*/
type Pick<T, K extends keyof T> = {
  // in 的肯定是一个联合类型
  [P in K]: T[P];
}
type PickType = Pick<IPerson,'name'|'gender'>; //{name: string, gender: number}

/** 5. Omit*/
type IPerson = {
  name: string;
  age: number;
  address: string;
  gender: number;
}

type Omit<T , K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type OmitType = Omit<IPerson, 'name'|'gender'>; //{age: number, address: string}
